<?php
/*
 * Copyright (c) 2022.  Liuhongbin All rights reserved.
 * @domain http://dkyx.cc
 * @author liuhongbin <514660094@qq.com>
 * @thank  xiaobo.sun@qq.com
 */

declare(strict_types=1);

namespace think\addons;

use think\exception\ClassNotFoundException;
use think\helper\Str;
use think\facade\Event;
use think\facade\Config;
use think\exception\HttpException;

class Route
{
    /**
     * 插件路由请求
     * @param null $addon
     * @param null $controller
     * @param null $action
     * @return mixed
     */
    public static function execute($addon = null, $controller = 'index', $action = 'index')
    {
        $app = app();
        $request = $app->request;
        //自动引入composer 和 common 文件
        self::auto_require($addon);
        $action = self::getAction($action, $request);
        Event::trigger('addons_begin', $request);
        if (empty($addon) || empty($controller) || empty($action)) {
            throw new HttpException(500, lang('addon can not be empty'));
        }
        $request->addon = $addon;
        // 设置当前请求的控制器、操作
        $request->setController($controller)->setAction($action);
        // 获取插件基础信息
        $info = get_addons_info($addon);
        if (!$info) {
            throw new HttpException(404, lang('addon %s not found', [$addon]));
        }
        if (!$info['status']) {
            throw new HttpException(500, lang('addon %s is disabled', [$addon]));
        }
        // 监听addon_module_init
        Event::trigger('addon_module_init', $request);
        $class = get_addons_class($addon, 'controller', $controller);
        if (!$class) {
            throw new HttpException(404, lang('addon %s controller %s not found', [$addon, Str::studly($controller)]));
        }

        // 重写视图基础路径
        $config = Config::get('view');
        $config['view_path'] = $app->addons->getAddonsPath() . $addon . DIRECTORY_SEPARATOR . 'view' . DIRECTORY_SEPARATOR;
        Config::set($config, 'view');
        // 生成控制器对象
        $instance = new $class($app);
        $vars = [];
        if (is_callable([$instance, $action])) {
            // 执行操作方法
            $call = [$instance, $action];
            try {
                $reflect = new \ReflectionMethod($class, $action);
                $vars = self::bindParams($reflect);
            } catch (ReflectionException $e) {
                throw new ClassNotFoundException('class not exists: ' . $class, $class, $e);
            }
        } elseif (is_callable([$instance, '_empty'])) {
            // 空操作
            $call = [$instance, '_empty'];
            $vars = [$action];
        } else {
            // 操作不存在
            throw new HttpException(404, lang('addon action %s not found', [get_class($instance) . '->' . $action . '()']));
        }
        Event::trigger('addons_action_begin', $call);
        return call_user_func_array($call, $vars);
    }


    /**
     * 绑定controller中方法的参数
     * @param ReflectionMethod $reflect
     * @return array
     */
    private static function bindParams(\ReflectionMethod $reflect)
    {
        $vars = request()->param();
        $args = [];
        if ($reflect->getNumberOfParameters() > 0) {
            // 判断数组类型 数字数组时按顺序绑定参数
            foreach ($reflect->getParameters() as $param) {
                $param_name = $param->name;
                if (array_key_exists($param_name, $vars)) {
                    $args[] = $vars[$param_name];
                }
            }
        }
        return $args;
    }

    private static function getAction($action, $request)
    {
        $vars = $request->rule()->getVars();
        $real_action = $vars['action'] ?? '';
        //action 可要可不要
        switch ($action) {
            case "[:action]":
                $action = $real_action ?: "index";
                break;
            case ":action":
                $action = $real_action ?: "index";
                break;
            default:
                break;
        }
        return $action;
    }

    private static function auto_require($addon){
        $addon_path = app()->getRootPath() . 'addons/' . $addon;
        $autoload =  $addon_path. '/vendor/autoload.php';
        if (file_exists($autoload)) require_once $autoload;
        $common =  $addon_path.'/common.php';
        if (file_exists($common)) require_once $common;
    }
}
